/**
 * Herbs
 * version 0.0.1
 * 
 * Class:Connection
 * This is an advanced version of a DataSocket.
 * Connections have functions for data flow management and buffering
 * 
 * Copyright (c) 2011 herbless.net
 * 
 * Herbs permits you to use, modify, and distribute this file
 * in accordance with the terms of the license agreement accompanying it.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **/

package com.sailorless.net 
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.ByteArray;
	import flash.utils.getTimer;

	public class Connection extends DataSocket 
	{
		//This is the protocol that will manage data when it is received
		private var _protocol:IProtocol;
		
		//This stack manages the current connection handler.
		//When data is received, it is sent off to the protocol policy object, 
		//which in turn sends it to the handler on the top of this stack.
		private var handlers:Vector.<ConnectionHandler>;
		
		//This is the handler on the top of handler stack
		private var _handler:ConnectionHandler;
		
		//The last time data was successfully sent.
		private var lastSendTime:int = 0;
		
		//The size of buffer data
		private var bufferSize:int = 0;
		
		private var maxSentBufferSize:int;
		private var maxSentTimeout:int;
		
		//This will dispatch timer event, manage the sent
		private var timerObj:Sprite;
		
		/**
		 * Constructors. 
		 * construct a normal connection,and use "Protocol" as a protocol object
		 * @param	Protocol  
		 * @param	maxSentTimeout
		 * @param	maxSentBufferSize
		 */
		public function Connection(Protocol:Class, maxSentTimeout:int = 500, maxSentBufferSize:int = 10240) 
		{
			_protocol = new Protocol() as IProtocol;
			handlers = new Vector.<ConnectionHandler>();
			timerObj = new Sprite();
			timerObj.addEventListener(Event.ENTER_FRAME, onManage);
			this.maxSentBufferSize = maxSentBufferSize;
			this.maxSentTimeout = maxSentTimeout;
		}
		
		/**
		 * Puts data into the sending queue.
		 * @param	data
		 */
		public function bufferData(data:ByteArray):void 
		{
			bufferSize += data.length;
			socket.writeBytes(data);
		}
		
		/**
		 * Sends the content of the send buffer
		 */
		public function sendBuffer():void 
		{
			if (bufferSize > 0)
			{
				bufferSize = 0;
				socket.flush();
				lastSendTime = getTimer();
			}
		}
		
		/**
		 * push the handler to stack
		 * @param	handler_
		 */
		public function addHandler(handler_:ConnectionHandler):void 
		{
			if (_handler != null)
				_handler.leave();		//leave the current handler if it exists
			
			handlers.push(handler_);
			_handler = handler_;
			
			if (isConnected)
				_handler.enter();			//enter the new handler
		}
		
		/**
		 * remove the current handler
		 */
		public function removeHandler():void 
		{
			if (_handler != null)
				_handler.leave();		//leave the current handler if it exists
			
			handlers.pop();				//pop the reference off
			
			if (handlers.length > 0)
			{
				//if handlers is not empty, set the current handler
				_handler = handlers[handlers.length - 1];	
				
				if (isConnected)
					_handler.enter();
			}
			else 
			{
				_handler = null;		//delete the current handler reference
			}
		}
		
		/**
		 * Whenever the socket received data, translate the data by protocol object
		 */
		override protected function connectReceive():void 
		{
			var buffer:ByteArray = new ByteArray();
			socket.readBytes(buffer);
			_protocol.translate(this, buffer);
		}
		
		/**
		 * Whenever the socket is connected, this function will be called
		 * @param	flag	the type of the error
		 */
		override protected function connectSuccess():void 
		{
			if (_handler != null)
				_handler.enter();
		}
		
		/**
		 * Whenever the socket connect failed, this function will be called
		 * @param	flag	the type of the error
		 */	
		override protected function connectFailed(flag:int):void 
		{
			if (_handler != null)
				_handler.hungUp(flag);
		}
		
		/**
		 * manage the buffer data, send it
		 * @param	e
		 */
		private function onManage(e:Event):void 
		{
			if (!isConnected || bufferSize == 0) 
				return; 
			if (bufferSize >= maxSentBufferSize || (getTimer() - lastSendTime) > maxSentTimeout)
				sendBuffer();
		}

// =================================================================================================
//  getter/setter
// =================================================================================================
		//The protocol object of this connection
		public function get protocol():IProtocol 
		{
			return _protocol;
		}
		
		//The current handler
		public function get handler():ConnectionHandler 
		{
			return _handler;
		}
		
	}

}